// Win Window
// (c) jimon game studio

#include "CWindow_Win.h"
#include "ILogManager.h"
#include "CMouseDriver.h"
#include "CKeyDriver.h"
#include "IVariables.h"

#ifdef JE_DEVICE_WIN

namespace je
{
	namespace device
	{
		u32 CWindow_Win::WindowClassID = 0;

		//! Constructor
		CWindow_Win::CWindow_Win(const jeVector2di & size,const jeStringd & Title,u1 fullscreen,s32 depth,s32 hertz,s32 msaa,s32 IconId,LRESULT CALLBACK WndProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam))
			:WindowHandle(NULL),appInstance(NULL),Size(size),FullScreenMode(fullscreen),CurrentDepth(depth),CurrentHertz(hertz),CurrentMSAA(msaa),WindowActived(false),MainWindow(false),NeedClose(false)
		{
			WindowClassID++;
			WindowClassName = jeStringd("Enesea_Win32_Window_")+jeStringd(WindowClassID);

			RECT WindowRect;
			WindowRect.left = 0;
			WindowRect.right = (long)Size.X;
			WindowRect.top = (long)0;
			WindowRect.bottom = (long)Size.Y;

			appInstance = GetModuleHandle(NULL);

			ZeroMemory(&DefaultMode, sizeof(DEVMODE));
			DefaultMode.dmSize = sizeof(DEVMODE);
			EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &DefaultMode);

			WNDCLASS wc;
			wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
			wc.lpfnWndProc = (WNDPROC)WndProc;
			wc.cbClsExtra = 0;
			wc.cbWndExtra = 0;
			wc.hInstance = appInstance;
			if(IconId != -1)
				wc.hIcon = LoadIcon(appInstance,MAKEINTRESOURCE(IconId));
			else
				wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
			wc.hCursor = LoadCursor(NULL, IDC_ARROW);
			wc.hbrBackground = NULL;
			wc.lpszMenuName = NULL;
			wc.lpszClassName = WindowClassName.c_str();

			if (!RegisterClass(&wc))
			{
				JELOG_E("Failed To Register Window Class")
				return;
			}

			if (FullScreenMode)
			{
				DEVMODE dmScreenSettings;
				memset(&dmScreenSettings,0,sizeof(dmScreenSettings));
				dmScreenSettings.dmSize = sizeof(dmScreenSettings);
				dmScreenSettings.dmPelsWidth = Size.X;
				dmScreenSettings.dmPelsHeight = Size.Y;
				dmScreenSettings.dmBitsPerPel = CurrentDepth;
				dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;

				if(hertz)
				{
					dmScreenSettings.dmDisplayFrequency = hertz;
					dmScreenSettings.dmFields |= DM_DISPLAYFREQUENCY;
				}

				if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
				{
					JELOG_E("Cant create requested fullscreen mode window, using window mode instead")
					FullScreenMode = false;
				}
			}

			DWORD dwExStyle;
			DWORD dwStyle;

			if (FullScreenMode)
			{
				dwExStyle = WS_EX_APPWINDOW;
				dwStyle = WS_POPUP;
			}
			else
			{
				dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
				dwStyle = WS_OVERLAPPEDWINDOW;
			}

			AdjustWindowRectEx(&WindowRect,dwStyle,false,dwExStyle);

			u1 Result = false;

			jeStringd SetTitle = Title;

			if (!(WindowHandle=CreateWindowEx(dwExStyle, WindowClassName.c_str(), SetTitle.c_str(),dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,0,0,WindowRect.right - WindowRect.left,WindowRect.bottom - WindowRect.top,NULL,NULL,appInstance,NULL)))
			{
				JELOG_E("Window Creation Error")
				return;
			}

			if (!(DeviceContext=GetDC(WindowHandle)))
			{
				JELOG_E("Device Context Creation Error")
				return;
			}

			ShowWindow(WindowHandle,SW_SHOW);
			SetForegroundWindow(WindowHandle);
			SetFocus(WindowHandle);

			JENEW(MouseDriver,input::CMouseDriver(this))
			JENEW(KeyDriver,input::CKeyDriver(this))
		}

		//! Destructor
		CWindow_Win::~CWindow_Win()
		{
			KeyDriver->Drop();
			MouseDriver->Drop();

			if (FullScreenMode)
				ChangeDisplaySettings(NULL,0);

			if (DeviceContext && !ReleaseDC(WindowHandle,DeviceContext))
				JELOG_E("Error at release device context")

			DeviceContext = NULL;

			if (WindowHandle)
			{
				SetWindowLong(WindowHandle,DWL_USER,(LONG)NULL);
				if(!DestroyWindow(WindowHandle))
					JELOG_E("Error at release window handle")
			}

			WindowHandle = NULL;

			if (!UnregisterClass(WindowClassName.c_str(),appInstance))
				JELOG_E("Error at unregister class")

			appInstance = NULL;
		}

		//! Get Win32 Window Handle
		HWND CWindow_Win::GetWin32WindowHandle()
		{
			return WindowHandle;
		}

		//! Get Win32 Device Context
		HDC CWindow_Win::GetWin32DeviceContext()
		{
			return DeviceContext;
		}

		//! Set Window Active
		void CWindow_Win::SetActive(u1 Active)
		{
			WindowActived = Active;
		}

		//! Set Close Flag
		void CWindow_Win::SetCloseFlag(u1 Close)
		{
			NeedClose = Close;
		}

		//! Resize Window
		void CWindow_Win::Resize(const jeVector2di & NewSize)
		{
			Size = NewSize;
		}

		//! Set Size
		void CWindow_Win::SetSize(const jeVector2di & NewSize)
		{
			jeVector2di Temp = GetWindowGlobalSize(FullScreenMode,NewSize);
			if(FullScreenMode)
				SetFullScreen(true,NewSize);
			else
				SetWindowPos(WindowHandle,HWND_TOP,0,0,Temp.X,Temp.Y,SWP_SHOWWINDOW | SWP_FRAMECHANGED);
			Size = NewSize;
		}

		//! Get Size
		const jeVector2di & CWindow_Win::GetSize()
		{
			return Size;
		}

		//! Set Window Position
		void CWindow_Win::SetPosition(const jeVector2di & NewPosition)
		{
			Position = NewPosition;

			RECT r;
			GetWindowRect(WindowHandle, &r);
			u32 SizeX = r.right - r.left;
			u32 SizeY = r.bottom - r.top;

			MoveWindow(WindowHandle,NewPosition.X,NewPosition.Y,SizeX,SizeY,true);
		}

		//! Get Window Position
		const jeVector2di & CWindow_Win::GetPosition()
		{
			return Position;
		}

		//! Set Fullscreen Mode
		void CWindow_Win::SetFullScreen(u1 FullScreen,const jeVector2di & NewSize,s32 Depth,s32 Hertz)
		{
			jeVector2di Temp = GetWindowGlobalSize(FullScreen,NewSize);
			if(FullScreen)
			{
				DEVMODE mode;
				ZeroMemory(&mode, sizeof(DEVMODE));
				mode.dmSize = sizeof(DEVMODE);
				mode.dmPelsWidth = NewSize.X;
				mode.dmPelsHeight = NewSize.Y;
				mode.dmBitsPerPel = Depth;
				CurrentDepth = Depth;
				mode.dmDisplayFrequency = Hertz;
				CurrentHertz = Hertz;
				mode.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_DISPLAYFREQUENCY;
				SetWindowLong(WindowHandle,GWL_EXSTYLE, WS_EX_APPWINDOW | WS_EX_TOPMOST);
				SetWindowLong(WindowHandle,GWL_STYLE, WS_POPUP);
				SetWindowPos(WindowHandle,HWND_TOPMOST, 0, 0,NewSize.X,NewSize.Y,SWP_SHOWWINDOW | SWP_FRAMECHANGED);
				if(ChangeDisplaySettings(&mode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
				{
					SetFullScreen(false,NewSize);
					return;
				}
			}
			else
			{
				SetWindowLong(WindowHandle,GWL_EXSTYLE,WS_EX_APPWINDOW);
				SetWindowLong(WindowHandle,GWL_STYLE,WS_OVERLAPPEDWINDOW);
				SetWindowPos(WindowHandle,HWND_TOP,0,0,Temp.X,Temp.Y, SWP_SHOWWINDOW | SWP_FRAMECHANGED);
				if(ChangeDisplaySettings(&DefaultMode, 0) != DISP_CHANGE_SUCCESSFUL)
				{
					JELOG_E("Cant change window settings to default")
					return;
				}
			}

			Size = NewSize;
			FullScreenMode = FullScreen;
		}

		//! Return true if fullscreen
		u1 CWindow_Win::IsFullScreen()
		{
			return FullScreenMode;
		}

		//! Get Depth
		s32 CWindow_Win::GetDepth()
		{
			return CurrentDepth;
		}

		//! Get Hertz
		s32 CWindow_Win::GetHertz()
		{
			return CurrentHertz;
		}

		//! Get MSAA
		s32 CWindow_Win::GetMSAA()
		{
			return CurrentMSAA;
		}

		//! Set Window Title
		void CWindow_Win::SetTitle(const jeStringd & Title)
		{
			SetWindowText(WindowHandle,Title.c_str());
		}

		//! Get Window Title
		jeStringd CWindow_Win::GetTitle()
		{
			jeschar Temp[2048];
			#ifdef JE_UNICODE
			GetWindowText(WindowHandle,(LPWSTR)&Temp,2048);
			#else
			GetWindowText(WindowHandle,(LPSTR)&Temp,2048);
			#endif
			jeStringd Title = Temp;
			return Title;
		}

		//! Set Main Window flag
		void CWindow_Win::SetMainWindow(u1 Main)
		{
			MainWindow = Main;
		}

		//! Return Main Window flag
		u1 CWindow_Win::IsMainWindow()
		{
			return MainWindow;
		}

		//! Return true if someone try to close window
		u1 CWindow_Win::IsNeedToClose()
		{
			return NeedClose;
		}

		//! Return true if window active
		u1 CWindow_Win::IsActive()
		{
			return WindowActived;
		}

		//! Set Focus on window
		void CWindow_Win::SetWindowFocus()
		{
			SetFocus(WindowHandle);
		}

		//! Get Mouse Driver
		input::IMouseDriver * CWindow_Win::GetMouseDriver()
		{
			return MouseDriver;
		}

		//! Get Key Driver
		input::IKeyDriver * CWindow_Win::GetKeyDriver()
		{
			return KeyDriver;
		}

		//! Get Window Api Pointer
		void * CWindow_Win::GetWindowApiPointer(WindowApiPointerType Type)
		{
			switch(Type)
			{
			default:
				return NULL;
			}
		}
	}
}

#endif
